package br.feevale.jogo.components;

import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import br.feevale.jogo.view.GameView;

public class GamePorta implements Runnable, SerialPortEventListener {

	private SerialPort porta;
	private CommPortIdentifier identifier;

	private String nomePorta;
	private int timeout;
	private int taxaTransmissao;

	private InputStream entrada;
	private OutputStream saida;
	private String dataReceived;

	private Thread threadLeitura;
	private GameView gameView;

	public GamePorta(String nomePorta, int timeout, int taxaTransmissao, GameView gameView) {
		this.nomePorta = nomePorta;
		this.timeout = timeout;
		this.taxaTransmissao = taxaTransmissao;
		this.gameView = gameView;

		getPortIdentifier();
		openPort();

		readData();
	}

	private void getPortIdentifier() {
		try {
			identifier = CommPortIdentifier.getPortIdentifier(nomePorta);

			if (identifier == null) {
				System.out.println("Erro na porta");
				System.exit(1);
			}
		} catch (NoSuchPortException e) {

			e.printStackTrace();
		}
	}

	private void openPort() {
		try {
			porta = (SerialPort) identifier.open("SerialComLeitura", timeout);

			porta.setSerialPortParams(taxaTransmissao, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
			porta.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
		} catch (Exception e) {
			System.out.println("Erro abrindo comunicação: " + e);
//			System.exit(1);
		}
	}

	private void closePort(){
		try {
			porta.close();
		} catch (Exception e) {
			System.out.println("Erro fechando porta: " + e);
			System.exit(0);
		}
	}
	
	private void readData() {
		try {
			entrada = porta.getInputStream();
		} catch (Exception e) {
			System.out.println("Erro de stream: " + e);
//			System.exit(1);
		}

		try {
			porta.addEventListener(this);
		} catch (Exception e) {
			System.out.println("Erro de listener: " + e);
//			System.exit(1);
		}

		porta.notifyOnDataAvailable(true);
		
		try {
			threadLeitura = new Thread(this);
			threadLeitura.start();
			run();
		} catch (Exception e) {
			System.out.println("Erro de Thread: " + e);
		}
	}

	@Override
	public void serialEvent(SerialPortEvent event) {
		StringBuffer bufferLeitura = new StringBuffer();
		int novoDado = 0;

		switch (event.getEventType()) {
		case SerialPortEvent.BI:
		case SerialPortEvent.OE:
		case SerialPortEvent.FE:
		case SerialPortEvent.PE:
		case SerialPortEvent.CD:
		case SerialPortEvent.CTS:
		case SerialPortEvent.DSR:
		case SerialPortEvent.RI:
		case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
			break;
		case SerialPortEvent.DATA_AVAILABLE:
			while (novoDado != -1) {
				try {
					novoDado = entrada.read();
					
					if (novoDado == -1) {
						break;
					}

					if ('\r' == (char) novoDado) {
						bufferLeitura.append('\n');
					} else {
						bufferLeitura.append((char) novoDado);
					}

				} catch (IOException ioe) {
					System.out.println("Erro de leitura serial: " + ioe);
				}
			}
			
			setDataReceived(new String(bufferLeitura));
			
			break;
		}
	}
	
	public String getDataReceived() {
		return dataReceived;
	}
	
	private void setDataReceived(String data) {
		this.dataReceived = data;
		System.out.println(data);
		gameView.dataReceived(data);
	}
	
	public void sendCommand(String command){
		try {
			saida = porta.getOutputStream();
		} catch (Exception e) {
			System.out.println("Erro.STATUS: " + e );
		}
		
		try {
			System.out.println("COMANDO : " + command );
			saida.write(command.getBytes());
			Thread.sleep(100);
			saida.flush();
			
		} catch (Exception e) {
			System.out.println("Houve um erro durante o envio. ");
			System.out.println("STATUS: " + e );
			System.exit(1);
		}
	}
	
	@Override
	public void run() {
		try {
			Thread.sleep(5);
		} catch (Exception e) {
			System.out.println("Erro de Thread: " + e);
		}
	}

}
